// Copyright (C) 2018 Chen XiaWen sup9@qq.com
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
#include "tool/request_creator_impl.h"

static void lock_cb(CURL *handle, curl_lock_data data, curl_lock_access access, void *userptr) {
  (void)access;  /* unused */
  (void)userptr; /* unused */
  std::mutex *p_mutex = (std::mutex *)userptr;
  (void)handle; /* unused */
  (void)data;   /* unused */
  p_mutex->lock();
}

static void unlock_cb(CURL *handle, curl_lock_data data, void *userptr) {
  (void)userptr; /* unused */
  std::mutex *p_mutex = (std::mutex *)userptr;
  (void)handle; /* unused */
  (void)data;   /* unused */
  p_mutex->unlock();
}

static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) {
  size_t realsize = size * nmemb;
  struct MemoryStruct *mem = (struct MemoryStruct *)userp;
  char *ptr = reinterpret_cast<char *>(realloc(mem->memory, mem->size + realsize + 1));
  if (ptr == NULL) {
    /* out of memory! */
    printf("not enough memory (realloc returned NULL)\n");
    return 0;
  }

  mem->memory = ptr;
  memcpy(&(mem->memory[mem->size]), contents, realsize);
  mem->size += realsize;
  mem->memory[mem->size] = 0;

  return realsize;
}

std::string RequestCreatorImpl::get_resource() { return p_chunk_->memory; }

bool RequestCreatorImpl::HttpGet(const char *url) {
  initurl init_url;
  init_url.url = url;
  p_chunk_ = std::make_shared<MemoryStruct>();
  CURL *curl = curl_easy_init();
  curl_easy_setopt(curl, CURLOPT_URL, url);
  curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);
  curl_easy_setopt(curl, CURLOPT_SHARE, share_);
  curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L);
  curl_easy_setopt(curl, CURLOPT_DNS_CACHE_TIMEOUT, 60 * 5);
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, reinterpret_cast<void *>(p_chunk_.get()));
  CURLcode res = curl_easy_perform(curl); /* ignores error */
                                          /* check for errors */
  if (res != CURLE_OK) {
    fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
  } else {
    /*
     * Now, our chunk.memory points to a memory block that is chunk.size
     * bytes big and contains the remote file.
     *
     * Do something nice with it!
     */
    printf("%lu bytes retrieved\n", (long unsigned int)p_chunk_->size);
  }
  curl_easy_cleanup(curl);
  return true;
}

RequestCreatorImpl::RequestCreatorImpl(/* args */) {
  /* Must initialize libcurl before any threads are started */
  curl_global_init(CURL_GLOBAL_ALL);
  share_ = curl_share_init();
  curl_share_setopt(share_, CURLSHOPT_LOCKFUNC, lock_cb);
  curl_share_setopt(share_, CURLSHOPT_UNLOCKFUNC, unlock_cb);
  curl_share_setopt(share_, CURLSHOPT_USERDATA, &curl_mutex_);
  curl_share_setopt(share_, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
  curl_share_setopt(share_, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
  InitLock();
}

RequestCreatorImpl::~RequestCreatorImpl() {
  KillLock();
  curl_share_cleanup(share_);
  curl_global_cleanup();
}

void RequestCreatorImpl::InitLock() {
  // curl_mutex_.try_lock();
}

void RequestCreatorImpl::KillLock() { curl_mutex_.unlock(); }
